package com.qztech.sdk.data;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * @author:zhanghb
 * @date:2019/1/25
 * @email:2573475062@qq.com
 **/
public class Resource<T> {

    private static final int LOADING = 1;
    private static final int ERROR = 2;
    private static final int SUCCESS = 3;
    private static final int NOT_CHANGED = 4;

    private final Throwable mError;
    private final int mState;
    private final T mData; //data or default data

    private Resource(Throwable error, T data, int state) {
        mError = error;
        mState = state;
        mData = data;
    }

    public boolean isSuccess() {
        return mState == SUCCESS;
    }

    public boolean isNoChange() {
        return mState == NOT_CHANGED;
    }

    public boolean isLoading() {
        return mState == LOADING;
    }

    public final boolean isError() {
        return mState == ERROR;
    }

    public final boolean hasData() {
        return mData != null;
    }

    public static <T> Resource<T> success() {
        return new Resource<>(null, null, SUCCESS);
    }

    public static <T> Resource<T> success(@Nullable T t) {
        return new Resource<>(null, t, SUCCESS);
    }

    public static <T> Resource<T> error(@NonNull Throwable error) {
        return error(error, null);
    }

    public static <T> Resource<T> error(@NonNull Throwable error, T defaultValue) {
        return new Resource<>(error, defaultValue, ERROR);
    }

    public static <T> Resource<T> loading() {
        return loading(null);
    }

    public static <T> Resource<T> loading(T defaultValue) {
        return new Resource<>(null, defaultValue, LOADING);
    }

    /**
     * 如果数据源(比如 Repository)缓存了上一次请求的数据，然后对其当前请求返回的数据，发现数据是一样的，可以使用此状态表示
     *
     * @return Resource
     */
    public static Resource noChange() {
        return new Resource<>(null, null, NOT_CHANGED);
    }

    @NonNull
    public T data() {
        if (isError() || isLoading() || isNoChange()) {
            throw new UnsupportedOperationException("when success state can call this method");
        }
        if (mData == null) {
            throw new NullPointerException("data is null");
        }
        return mData;
    }

    @Nullable
    public T orElse(@Nullable T elseData) {
        if (isError() || isLoading() || isNoChange()) {
            throw new UnsupportedOperationException("Call this method when success state");
        }
        if (mData == null) {
            return elseData;
        }
        return mData;
    }

    @Nullable
    public T defaultData() {
        if (!isError() || !isLoading()) {
            throw new UnsupportedOperationException("Call this method when loading or error state");
        }
        return mData;
    }

    public Throwable error() {
        return mError;
    }

    @NonNull
    @Override
    public String toString() {
        return "Resource{" +
                "mError=" + mError +
                ", mState=" + mState +
                ", mData=" + mData +
                '}';
    }

}